﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MagicCove
{
    //Note is playable does not account for mana cost, that is handled elsewhere, this is assuming that the mana cost has been satisfied

    //IMPORTANT iPBase will not work properly if it is not contructed with a specialEffect (or magic card), as the specialEffect contruction will set its isPlayable to be itself
    class iPBase
    {//It is bad practice to create a class of iPBase. One should create one of iPAlways for clarity and consistentency
        private sEBase sEC; //the special effect that corresponds with this iP (if it corresponds to an sE
        private MagicCard parentCard; //the card is iP belongs to
        private List<ManaType> manaCost; //note, empty list indicates cost has been reduced, a null list indicates it didn't cost anything to begin with
        public sEBase ParentSE
        {
            get
            {
                return this.sEC;
            }
            set
            {
                this.sEC = value;
            }
        }
        public MagicCard ParentCard
        {
            get
            {
                return parentCard;
            }
            set
            {
                this.parentCard = value;
            }
        }
        public List<ManaType> ManaCost
        {
            get
            {
                return this.manaCost;
            }
        }

        public iPBase() { manaCost = null; }
        public iPBase(List<ManaType> manaCost)
        {
            this.manaCost = manaCost;
        }
        public virtual bool isPlayable()
        {
            throw new System.Exception("This function should never be called. (isPlayable base class)");
        }
        public int[] getManaLeft()
        {//will try to select the best floating mana. ~!~ if more mana is tapped then need should prompt the user as to what mana they want, rather than guessing
            int[] manaLeft = (int[])ParentCard.ParentField.Mana.Clone();
            if (manaCost == null) //ability does not cost mana
                return manaLeft;
            //List<ManaType> manaNotPaid = new List<ManaType>(manaCost); //something like this can be used when dual colored mana is supported
            //pay regular mana
            foreach (ManaType manaElement in manaCost)
            {//~!~ this currently only deals with single colored. (not optimized for dual colors)
                if ((manaElement & ManaType.White) != 0)
                    manaLeft[1]--;
                else if ((manaElement & ManaType.Blue) != 0)
                    manaLeft[2]--;
                else if ((manaElement & ManaType.Black) != 0)
                    manaLeft[3]--;
                else if ((manaElement & ManaType.Red) != 0)
                    manaLeft[4]--;
                else if ((manaElement & ManaType.Green) != 0)
                    manaLeft[5]--;
                else if ((manaElement & ManaType.TwoColorless) != 0)
                    manaLeft[0] -= 2;
                else if ((manaElement & ManaType.ThreeColorless) != 0)
                    manaLeft[0] -= 3;
                else
                    manaLeft[0]--;
            }
            //balance array by shifting leftover colered mana into colorless
            while (manaLeft[0] < 0 && (manaLeft[1] > 0 || manaLeft[2] > 0 || manaLeft[3] > 0 || manaLeft[4] > 0 || manaLeft[5] > 0)) //pay mana for colorless mana
            {
                manaLeft[0]++;
                if (manaLeft[1] > 0)
                    manaLeft[1]--;
                else if (manaLeft[2] > 0)
                    manaLeft[2]--;
                else if (manaLeft[3] > 0)
                    manaLeft[3]--;
                else if (manaLeft[4] > 0)
                    manaLeft[4]--;
                else
                    manaLeft[5]--;
            }
            return manaLeft;
        }
    }
    #region Mechanic IPs
    class iPMetalcraftTriggered : iPBase
    {//for triggered abilities with metalcraft.
        bool metalCraftTriggered;
        public iPMetalcraftTriggered()
            : base() { metalCraftTriggered = false; }
        public override bool isPlayable()
        {//could be made slightly more efficient with a break
            int metalCraftCounter = 0;
            foreach (MagicCard mC in ParentCard.ParentField.field)
                if ((mC.CardTypes & CardType.Artifact) != 0)
                    metalCraftCounter++;
            if (!metalCraftTriggered && metalCraftCounter >= 3)
                return true;
            else if (metalCraftTriggered && metalCraftCounter < 3)
                ParentSE.removeSpecialEffect();
            return false;
        }
    }

    #endregion
    #region Other IPs
    class iPAlways : iPBase
    { //sE always triggers
        public iPAlways()
            : base() { }
        public iPAlways(List<ManaType> manaCost)
            : base(manaCost) { }
        public override bool isPlayable()
        {
            return true;
        }
    }
    class iPAttackingCreatures : iPBase
    {
        public iPAttackingCreatures()
            : base() { }
        public iPAttackingCreatures(List<ManaType> manaCost)
            : base(manaCost) { }
        public override bool isPlayable()
        {
            if (ParentCard.ParentField.ParentGameForm.AttackingCreatures.Count != 0)
            {
                ParentCard.targets = ParentCard.ParentField.ParentGameForm.AttackingCreatures;
                return true;
            }
            return false;
        }
    }
    class iPACEPST : iPBase //regular Artifact/Creature/Enchantment/Planeswalker/Sorcery/Tribal
    {
        public iPACEPST()
            : base() { }
        public iPACEPST(List<ManaType> manaCost)
            : base(manaCost) { }
        override public bool isPlayable()
        {
            if ((ParentCard.ParentField.ParentGameForm.CurrentPhase == Phase.PreCombatMain || ParentCard.ParentField.ParentGameForm.CurrentPhase == Phase.PostCombatMain) 
                && ParentCard.ParentField.ParentGameForm.stack.Count == 0)
                return true;
            return false;
        }
    }
    class iPLandPlay : iPBase //land card
    {
        public override bool isPlayable()
        {
            if (!ParentCard.ParentField.landPlayed 
                && (ParentCard.ParentField.ParentGameForm.CurrentPhase == Phase.PreCombatMain || ParentCard.ParentField.ParentGameForm.CurrentPhase == Phase.PostCombatMain) 
                && ParentCard.ParentField.ParentGameForm.stack.Count == 0)
            {
                ParentCard.LocationGoing = CurrentLocation.Field;
                ParentCard.ParentField.landPlayed = true;
                return true;
            }
            return false;
        }
    }
    class iPEntersField : iPBase
    {//~!~ Needs to be corrected to place the ability on the stack
        bool goingToField = false;
        public override bool isPlayable()
        {
            if (goingToField && ParentCard.Location == CurrentLocation.Field)
                return true;
            goingToField = false; //if anything but going to field happens it needs to be changed back
            if (ParentCard.LocationGoing == CurrentLocation.Field)
            {
                goingToField = true;
                return true;
            }
            return false;
        }
    }
    class iPIsAttacking : iPBase
    {//if the card is attacking
        public override bool isPlayable()
        {
            return (ParentCard.ParentField.ParentGameForm.AttackingCreatures.Contains(ParentSE.ParentCard) && ParentCard.ParentField.ParentGameForm.CurrentPhase == Phase.DeclareAttackers);
        }
    }
    class iPManaAbility : iPBase //land card
    {
        public override bool isPlayable()
        {//just prevent ability from going on stack
            if (ParentCard.Location == CurrentLocation.Field && ParentCard.tapped == false) //otherwise the land cannot be tapped
            {
                ParentCard.ParentField.ParentGameForm.stack.Pop();
                ParentSE.popedFromStack();
                return true;
            }
            return false;
        }
    }
    class iPSacrificeCard : iPBase
    { //this really will just sacrifice the card
        public override bool isPlayable()
        {
            if (ParentCard.Location == CurrentLocation.Field)
            {
                ParentCard.LocationGoing = CurrentLocation.Graveyard;
                return true;
            }
            return false;
        }
    }
    class iPTargetTypes : iPBase
    {
        CardType cardTypes; //card types that must be targeted
        CardType nonCardTypes; //card types that cannot be targeted
        List<int> targetNumbers; //number of targets. If the array is empty it is assumed that any number of targets can be chosen
        TargetLocation targetLocation;
        public iPTargetTypes(CardType cardTypes, CardType nonCardTypes, List<int> targetNumbers)
            : base() { this.cardTypes = cardTypes; this.nonCardTypes = nonCardTypes; this.targetNumbers = targetNumbers; this.targetLocation = TargetLocation.None; }
        public iPTargetTypes(List<ManaType> manaCost, CardType cardTypes, CardType nonCardTypes, List<int> targetNumbers)
            : base(manaCost) { this.cardTypes = cardTypes; this.nonCardTypes = nonCardTypes; this.targetNumbers = targetNumbers; this.targetLocation = TargetLocation.None; }
        public iPTargetTypes(CardType cardTypes, CardType nonCardTypes, List<int> targetNumbers, TargetLocation targetLocation)
            : base() { this.cardTypes = cardTypes; this.nonCardTypes = nonCardTypes; this.targetNumbers = targetNumbers; this.targetLocation = targetLocation; }
        public iPTargetTypes(List<ManaType> manaCost, CardType cardTypes, CardType nonCardTypes, List<int> targetNumbers, TargetLocation targetLocation)
            : base(manaCost) { this.cardTypes = cardTypes; this.nonCardTypes = nonCardTypes; this.targetNumbers = targetNumbers; this.targetLocation = targetLocation; }
        public override bool isPlayable()
        {//function could be slightly optimized by using only one bool. However this form improves readability.
            bool targetNumberCorrect = false;
            foreach (int i in targetNumbers)
                if (i == ParentCard.targets.Count)
                {
                    targetNumberCorrect = true;
                    break;
                }
            if (!targetNumberCorrect)
            {
                System.Windows.Forms.MessageBox.Show("You must select the correct number of targets for this card before playing it.");
                return false;
            }
            if (targetLocation != TargetLocation.None) //then need to check if target locations are correct
            {
                foreach (TargetLocation tL in ParentCard.TargetLocations)
                    if ((tL & targetLocation) == 0)
                    {
                        System.Windows.Forms.MessageBox.Show("You must select an target in the appropriate location before playing this card.");
                        return false;
                    }
                //otherwise continue with checks
            }
            foreach (MagicCard mC in ParentCard.targets)
                if ((mC.CardTypes & cardTypes) == 0)
                    return false;
            foreach (MagicCard mC in ParentCard.targets)
                if ((mC.CardTypes & nonCardTypes) != 0)
                    return false;
            return true;
        }
    }
    class iPResolvingFromStack : iPBase
    {
        public override bool isPlayable()
        {
            if (ParentCard.Location == CurrentLocation.Stack && ParentCard.LocationGoing != CurrentLocation.Stack)
                return true;
            return false;
        }
    }
    #endregion
}
